package com.wimcorp.magic.test;

import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTransactionalTestNGSpringContextTests;
import org.testng.annotations.Test;

import com.wimcorp.magic.abilities.Ability;
import com.wimcorp.magic.abilities.AddBehavior;
import com.wimcorp.magic.abilities.AddCardCountCounter;
import com.wimcorp.magic.abilities.AddCounter;
import com.wimcorp.magic.abilities.AddManaToPool;
import com.wimcorp.magic.abilities.ComboAbility;
import com.wimcorp.magic.abilities.ConditionAbility;
import com.wimcorp.magic.abilities.ControlCreature;
import com.wimcorp.magic.abilities.CopyCreature;
import com.wimcorp.magic.abilities.CounterSpell;
import com.wimcorp.magic.abilities.DamageTarget;
import com.wimcorp.magic.abilities.DestroyTarget;
import com.wimcorp.magic.abilities.DrawCards;
import com.wimcorp.magic.abilities.FutureSight;
import com.wimcorp.magic.abilities.Life;
import com.wimcorp.magic.abilities.Regenerate;
import com.wimcorp.magic.abilities.Return;
import com.wimcorp.magic.abilities.RevealDeckAndChoose;
import com.wimcorp.magic.abilities.RevealDeckKeepTypeAndReturnInAnyOrder;
import com.wimcorp.magic.abilities.RevealHand;
import com.wimcorp.magic.abilities.Sacrifice;
import com.wimcorp.magic.abilities.Scry;
import com.wimcorp.magic.abilities.Suspend;
import com.wimcorp.magic.abilities.trigger.AbilityTrigger;
import com.wimcorp.magic.abilities.trigger.ComboTrigger;
import com.wimcorp.magic.abilities.trigger.ConditionTrigger;
import com.wimcorp.magic.abilities.trigger.OnAttack;
import com.wimcorp.magic.abilities.trigger.OnBlocked;
import com.wimcorp.magic.abilities.trigger.OnCombatDamageToCreature;
import com.wimcorp.magic.abilities.trigger.OnCombatDamageToPlayer;
import com.wimcorp.magic.abilities.trigger.OnCreatureDeath;
import com.wimcorp.magic.abilities.trigger.OnEnterBattlefield;
import com.wimcorp.magic.abilities.trigger.OnPlayerCast;
import com.wimcorp.magic.abilities.trigger.OnSpellTarget;
import com.wimcorp.magic.abilities.trigger.OnSuspendTimeCounterReduced;
import com.wimcorp.magic.abilities.trigger.OnUnSuspend;
import com.wimcorp.magic.abilities.trigger.OnUpkeepPlayer;
import com.wimcorp.magic.abilities.trigger.Passive;
import com.wimcorp.magic.behaviour.Behaviour;
import com.wimcorp.magic.board.PlayingDeck;
import com.wimcorp.magic.board.PlayingDeckPlayingCard;
import com.wimcorp.magic.cards.Artifact;
import com.wimcorp.magic.cards.BasicLand;
import com.wimcorp.magic.cards.Creature;
import com.wimcorp.magic.cards.Enchantment;
import com.wimcorp.magic.cards.Instant;
import com.wimcorp.magic.cards.Planeswalker;
import com.wimcorp.magic.cards.Sorcery;
import com.wimcorp.magic.color.Color;
import com.wimcorp.magic.cost.Cost;
import com.wimcorp.magic.cost.ManaCost;
import com.wimcorp.magic.cost.PayLife;
import com.wimcorp.magic.cost.ReturnCost;
import com.wimcorp.magic.cost.SacrificeCreature;
import com.wimcorp.magic.cost.TapCard;
import com.wimcorp.magic.dao.PlayingCardDao;
import com.wimcorp.magic.dao.PlayingDeckDao;
import com.wimcorp.magic.data.AbilityData;
import com.wimcorp.magic.data.BehaviourData;
import com.wimcorp.magic.data.CardData;
import com.wimcorp.magic.data.ColorData;
import com.wimcorp.magic.data.CostData;
import com.wimcorp.magic.data.DeckData;
import com.wimcorp.magic.data.SubTypeData;
import com.wimcorp.magic.data.TypeData;
import com.wimcorp.magic.data.dao.DeckDataDao;
import com.wimcorp.magic.exception.NotImplementedException;
import com.wimcorp.magic.exception.SystemException;
import com.wimcorp.magic.input.AbilityInput;
import com.wimcorp.magic.input.AbilityTriggerInput;
import com.wimcorp.magic.input.BehaviorInput;
import com.wimcorp.magic.input.Condition;
import com.wimcorp.magic.input.CostInput;
import com.wimcorp.magic.input.Duration;
import com.wimcorp.magic.input.Input;
import com.wimcorp.magic.input.Location;
import com.wimcorp.magic.input.Target;
import com.wimcorp.magic.input.Type;
import com.wimcorp.magic.subtypes.SubType;

@Test
@ContextConfiguration(locations = { "/applicationContext-magic-the-androiding.xml" })
public class LoadPlayingDeck extends AbstractTransactionalTestNGSpringContextTests {

	@Autowired
	private PlayingCardDao playingCardDao;

	@Autowired
	private PlayingDeckDao playingDeckDao;

	@Autowired
	private DeckDataDao deckDataDao;

	@Rollback(false)
	public void testDOA() throws IOException {

		try {

			Set<CardData> jacaCardData = createJaceCardData();
			createDeck("Jace", jacaCardData);

			Set<CardData> vraskaCardData = createVraskaCardData();
			createDeck("Vraska", vraskaCardData);

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private Set<CardData> createJaceCardData() {
		DeckData deck = deckDataDao.getDeck("DDM");
		Set<CardData> result = new HashSet<>();
		Set<CardData> cardz = deck.getCards();
		for (CardData card : cardz) {
			if (card.getNumber() < 42 && card.getNumber() != 56) {
				if (card.isVerified()) {
					result.add(card);
				}
			}
		}
		return result;
	}

	private Set<CardData> createVraskaCardData() {
		DeckData deck = deckDataDao.getDeck("DDM");
		Set<CardData> result = new HashSet<>();
		Set<CardData> cardz = deck.getCards();
		for (CardData card : cardz) {
			if (card.getNumber() >= 42) {
				if (card.isVerified()) {
					result.add(card);
				}
			}
		}
		return result;
	}

	private void createDeck(String name, Set<CardData> cards) throws NotImplementedException, SystemException {
		PlayingDeck deck = new PlayingDeck(name);

		List<PlayingDeckPlayingCard> playingCards = new ArrayList<PlayingDeckPlayingCard>();
		playingDeckDao.saveOrUpdate(deck);

		for (CardData card : cards) {
			// CardData card = cards.get(i);
			Set<TypeData> types = card.getTypes();
			if (types.size() == 1) {
				for (TypeData t : types) {
					String type = t.getType();
					if ("Creature".equals(type)) {
						Creature playingCard = loadCreature(card);
						playingCardDao.saveOrUpdate(playingCard);
						playingCards.add(new PlayingDeckPlayingCard(deck, playingCard, card.getCount()));

					} else if ("Enchantment".equals(type)) {
						Enchantment playingCard = loadEnchantement(card);
						playingCardDao.saveOrUpdate(playingCard);
						playingCards.add(new PlayingDeckPlayingCard(deck, playingCard, card.getCount()));

					} else if ("Land".equals(type)
							&& ("Forest".equals(card.getName()) || "Swamp".equals(card.getName())
									|| "Island".equals(card.getName()) || "Plains".equals(card.getName()) || "Mountain"
										.equals(card.getName()))) {
						// only load basic lands for now
						BasicLand playingCard = loadLand(card);
						playingCardDao.saveOrUpdate(playingCard);
						playingCards.add(new PlayingDeckPlayingCard(deck, playingCard, card.getCount()));

					} else if ("Instant".equals(type)) {
						Instant playingCard = loadInstant(card);
						playingCardDao.saveOrUpdate(playingCard);
						playingCards.add(new PlayingDeckPlayingCard(deck, playingCard, card.getCount()));

					} else if ("Sorcery".equals(type)) {
						Sorcery playingCard = loadSorcery(card);
						playingCardDao.saveOrUpdate(playingCard);
						playingCards.add(new PlayingDeckPlayingCard(deck, playingCard, card.getCount()));

					} else if ("Artifact".equals(type)) {
						Artifact playingCard = loadArtifact(card);
						playingCardDao.saveOrUpdate(playingCard);
						playingCards.add(new PlayingDeckPlayingCard(deck, playingCard, card.getCount()));

					} else if ("Planeswalker".equals(type)) {
						Planeswalker playingCard = loadPlaneswalker(card);
						playingCardDao.saveOrUpdate(playingCard);
						playingCards.add(new PlayingDeckPlayingCard(deck, playingCard, card.getCount()));

					}

				}
			} else {
				// throw new
				// NotImplementedException("Cards with multiple types aren't supported yet... ");
			}

			// PlayingCard playingCard = new PlayingCard();
		}
		deck.addCards(playingCards);
		playingDeckDao.saveOrUpdate(deck);

	}

	private Creature loadCreature(CardData card) throws SystemException {
		Set<SubTypeData> subtypes = card.getSubtypes();
		Set<SubType> creatureSubtypes = new HashSet<SubType>();
		for (SubTypeData subtype : subtypes) {
			try {
				Class<?> c = Class.forName("com.wimcorp.magic.subtypes." + subtype.getSubtype());
				Method factoryMethod = c.getDeclaredMethod("instance");
				creatureSubtypes.add((SubType) factoryMethod.invoke(null, null));

			} catch (Exception e) {
				e.printStackTrace();
				throw new SystemException("Error while loading subtypes");
			}

		}
		Set<Color> cardColors = calculateCardColors(card);

		ManaCost manaCost = calculateManaCost(card.getManacost());

		Set<BehaviourData> behaviours = card.getBehaviours();
		Set<Behaviour> creatureBehaviours = new HashSet<Behaviour>();
		for (BehaviourData behaviour : behaviours) {
			try {
				Class<?> c = Class.forName("com.wimcorp.magic.behaviour."
						+ BehaviorInput.valueOf(behaviour.getBehaviour()).getName());
				Method factoryMethod = c.getDeclaredMethod("instance");
				creatureBehaviours.add((Behaviour) factoryMethod.invoke(null, null));
			} catch (Exception e) {
				try {
					Class<?> c = Class.forName("com.wimcorp.magic.behaviour.attack."
							+ BehaviorInput.valueOf(behaviour.getBehaviour()).getName());
					Method factoryMethod = c.getDeclaredMethod("instance");
					creatureBehaviours.add((Behaviour) factoryMethod.invoke(null, null));
				} catch (Exception ee) {
					ee.printStackTrace();
					throw new SystemException("Error while loading behaviours");
				}
			}

		}
		Set<AbilityData> abilities = card.getAbilities();
		Set<Ability> creatureAbilities = new HashSet<Ability>();
		for (AbilityData a : abilities) {
			Ability creatAbility = createAbility(a);
			if (creatAbility != null) {
				creatureAbilities.add(creatAbility);
			} else {
				System.out.println("ability not found " + a.getId() + " " + a.getAbility());
			}
		}
		for (Ability ability : creatureAbilities) {
			if (ability.getTrigger() instanceof OnUnSuspend
					|| ability.getTrigger() instanceof OnSuspendTimeCounterReduced) {
				Suspend suspenAbility = null;
				for (Ability ability2 : creatureAbilities) {
					if (ability2 instanceof Suspend) {
						suspenAbility = (Suspend) ability2;
					}
				}
				if (ability.getTrigger() instanceof OnUnSuspend) {
					((OnUnSuspend) ability.getTrigger()).setSuspendAbility(suspenAbility);
				} else if (ability.getTrigger() instanceof OnSuspendTimeCounterReduced) {
					((OnSuspendTimeCounterReduced) ability.getTrigger()).setSuspendAbility(suspenAbility);
				}
			}
		}
		return new Creature(card.getMultiverseid(), card.getName(), creatureSubtypes, card.getText(), cardColors,
				card.getPower(), card.getToughness(), creatureBehaviours, creatureAbilities, manaCost);
	}

	private Ability createAbility(AbilityData a) {
		Ability ability = null;
		try {
			AbilityInput abilityInput = AbilityInput.valueOf(a.getAbility());

			JSONParser parser = new JSONParser();
			JSONObject abilityConfig = (JSONObject) parser.parse(a.getConfig());
			switch (abilityInput) {
			case ADD_BEHAVIOR:
				ability = new AddBehavior(createAbilityTrigger(abilityConfig));
				((AddBehavior) ability)
						.setBehaviorInput(BehaviorInput.valueOf(toString(abilityConfig.get("behavior"))));
				break;
			case ADD_CARD_COUNT_COUNTER:
				ability = new AddCardCountCounter(createAbilityTrigger(abilityConfig));
				((AddCardCountCounter) ability).setCounterLocation(Location.valueOf(toString(abilityConfig
						.get("location"))));
				((AddCardCountCounter) ability).setCounterTarget(Target.valueOf(toString(abilityConfig.get("target"))));
				((AddCardCountCounter) ability).setCounterType(Type.valueOf(toString(abilityConfig.get("type"))));
				break;
			case ADD_COUNTER:
				ability = new AddCounter(createAbilityTrigger(abilityConfig));
				((AddCounter) ability).setPower(new Integer(toString(abilityConfig.get("power"))));
				((AddCounter) ability).setToughness(new Integer(toString(abilityConfig.get("toughness"))));
				break;
			case ADD_MANA_TO_POOL:
				ability = new AddManaToPool(createAbilityTrigger(abilityConfig));
				((AddManaToPool) ability).setAmount(new Integer(toString(abilityConfig.get("amount"))));
				ArrayList<String> colorOptions = new ArrayList<String>();
				JSONArray co = (JSONArray) abilityConfig.get("colorOptions");
				for (int i = 0; i < co.size(); i++) {
					colorOptions.add(co.get(i).toString());
				}
				((AddManaToPool) ability).setColorOptions(colorOptions);
				break;
			case CONTROL_CREATURE:
				ability = new ControlCreature(createAbilityTrigger(abilityConfig));
				break;
			case COPY_CREATURE:
				ability = new CopyCreature(createAbilityTrigger(abilityConfig));
				break;
			case COUNTER_SPELL:
				ability = new CounterSpell(createAbilityTrigger(abilityConfig));
				((CounterSpell) ability).setDestination(Location.valueOf(toString(abilityConfig.get("destination"))));
				break;
			case DAMAGE_TARGET:
				ability = new DamageTarget(createAbilityTrigger(abilityConfig));
				((DamageTarget) ability).setAmount(new Integer(toString(abilityConfig.get("amount"))));
				break;
			case DESTROY_TARGET:
				ability = new DestroyTarget(createAbilityTrigger(abilityConfig));
				break;
			case DRAW_CARDS:
				ability = new DrawCards(createAbilityTrigger(abilityConfig));
				((DrawCards) ability).setAmount(new Integer(toString(abilityConfig.get("amount"))));
				break;
			case FUTURE_SIGHT:
				ability = new FutureSight(createAbilityTrigger(abilityConfig));
				break;
			/*
			 * case KICKER_ABILITY: ability = new
			 * KickerAbility(createAbilityTrigger(abilityConfig)); break;
			 */
			case LIFE:
				ability = new Life(createAbilityTrigger(abilityConfig));
				((Life) ability).setAmount(new Integer(toString(abilityConfig.get("amount"))));
				break;
			case REGENERATE:
				ability = new Regenerate(createAbilityTrigger(abilityConfig));
				break;
			case RETURN:
				ability = new Return(createAbilityTrigger(abilityConfig));
				((Return) ability).setDestination(Location.valueOf(toString(abilityConfig.get("destination"))));
				break;
			case REVEAL_DECK_AND_CHOOSE:
				ability = new RevealDeckAndChoose(createAbilityTrigger(abilityConfig));
				((RevealDeckAndChoose) ability).setChosenDestination(Location.valueOf(toString(abilityConfig
						.get("chosenDestination"))));
				((RevealDeckAndChoose) ability).setUnChosenDestination(Location.valueOf(toString(abilityConfig
						.get("unChosenDestination"))));
				((RevealDeckAndChoose) ability).setAmount(new Integer(toString(abilityConfig.get("amount"))));
				break;
			case REVEAL_DECK_KEEP_TYPE_AND_RETURN_IN_ANY_ORDER:
				ability = new RevealDeckKeepTypeAndReturnInAnyOrder(createAbilityTrigger(abilityConfig));
				((RevealDeckKeepTypeAndReturnInAnyOrder) ability).setKeepDestination(Location
						.valueOf(toString(abilityConfig.get("keepDestination"))));
				((RevealDeckKeepTypeAndReturnInAnyOrder) ability).setReturnDestination(Location
						.valueOf(toString(abilityConfig.get("returnDestination"))));
				((RevealDeckKeepTypeAndReturnInAnyOrder) ability).setAmount(new Integer(toString(abilityConfig
						.get("amount"))));
				break;
			case REVEAL_HAND:
				ability = new RevealHand(createAbilityTrigger(abilityConfig));
				break;
			case SACRIFICE:
				ability = new Sacrifice(createAbilityTrigger(abilityConfig));
				break;
			case SCRY:
				ability = new Scry(createAbilityTrigger(abilityConfig));
				((Scry) ability).setScry(new Integer(toString(abilityConfig.get("scry"))));
				break;
			case SUSPEND:
				ability = new Suspend(createAbilityTrigger(abilityConfig));
				((Suspend) ability).setTimeCounter(new Integer(toString(abilityConfig.get("timeCounter"))));
				break;
			case COMBO_ABILITY:
				List<Ability> abilties = new ArrayList<>();
				JSONObject ab = null;
				int i = 1;
				do {
					ab = (JSONObject) abilityConfig.get("ability" + i);
					if (ab != null) {
						abilties.add(createAbility(new AbilityData(toString(ab.get("name")), ab.get("config")
								.toString(), new HashSet<CostData>())));
					}
					i++;
				} while (ab != null);
				ability = new ComboAbility(new ComboTrigger(), abilties,Input.valueOf(toString(abilityConfig.get("inputOption"))));
				break;
			case CONDITION_ABILITY:
				Ability nonConditionAbility = null;
				JSONObject nonConAb = (JSONObject) abilityConfig.get("nonConditionAbility");
				if (nonConAb != null) {
					nonConditionAbility = createAbility(new AbilityData(toString(nonConAb.get("name")), nonConAb.get(
							"config").toString(), new HashSet<CostData>()));
				}
				Ability conditionAbility = null;

				JSONObject condAb = (JSONObject) abilityConfig.get("conditionAbility");
				if (condAb != null) {
					conditionAbility = createAbility(new AbilityData(toString(condAb.get("name")), condAb.get("config")
							.toString(), new HashSet<CostData>()));
				}
				ability = new ConditionAbility(new ConditionTrigger(), nonConditionAbility, conditionAbility,Condition.valueOf(toString(abilityConfig.get("condition"))));
				break;
			}
			Object values = abilityConfig.get("target");
			if (values != null) {
				ability.setTarget(Target.valueOf(toString(values)));
			}
			values = abilityConfig.get("duration");
			if (values != null) {
				ability.setDuration(Duration.valueOf(toString(values)));
			}
			values = abilityConfig.get("types");
			if (values != null) {
				ability.setTypes(new HashSet<String>((JSONArray) values));
			}
			values = abilityConfig.get("locations");
			if (values != null) {
				ability.setLocations(new HashSet<String>((JSONArray) values));
			}
			values = abilityConfig.get("behaviors");
			if (values != null) {
				ability.setBehaviors(new HashSet<String>((JSONArray) values));
			}
			if (abilityConfig.get("description") != null) {
				ability.setDescription(toString(abilityConfig.get("description")));
			}
			for (CostData costData : a.getCosts()) {
				ability.addCost(createCost(costData));
			}

		} catch (Exception e) {
			System.out.println(a.getAbility());
			System.out.println(a.getConfig());
			e.printStackTrace();

		}
		return ability;
	}

	private Cost createCost(CostData costData) {
		Cost cost = null;
		JSONParser parser = new JSONParser();
		JSONObject costConfig = null;
		try {
			costConfig = (JSONObject) parser.parse(costData.getConfig());
		} catch (ParseException e) {

		}
		switch (CostInput.valueOf(costData.getCost())) {
		case MANA_COST:
			String manaCost = toString(costConfig.get("manaCost"));
			ManaCost calculateManaCost = calculateManaCost(manaCost);
			Set<Color> colorsInPool = calculateManaCost.getColorsInPool();
			cost = new ManaCost(colorsInPool.toArray(new Color[colorsInPool.size()]));
			break;
		case PAYLIFE:
			cost = new PayLife(new Integer(toString(costConfig.get("amount"))));
			break;
		case RETURN_COST:
			cost = new ReturnCost(Type.valueOf(toString(costConfig.get("type"))), Location.valueOf(toString(costConfig
					.get("location"))), Location.valueOf(toString(costConfig.get("destination"))));
			break;
		case SACRIFICE_CREATURE:
			cost = new SacrificeCreature();
			break;
		case TAP_CARD:
			cost = new TapCard();
			break;
		default:
			break;
		}
		return cost;

	}

	private AbilityTrigger createAbilityTrigger(JSONObject abilityConfig) throws ClassNotFoundException,
			InstantiationException, IllegalAccessException {
		AbilityTrigger abilityTrigger = null;
		String trigger = abilityConfig.get("trigger").toString().replace("\"", "");
		switch (AbilityTriggerInput.valueOf(trigger.substring(1, trigger.length() - 1))) {
		case ON_ATTACK:
			abilityTrigger = new OnAttack();
			break;
		case ON_COMBAT_DAMAGE_TO_CREATURE:
			abilityTrigger = new OnCombatDamageToCreature();
			break;
		case ON_COMBAT_DAMAGE_TO_PLAYER:
			abilityTrigger = new OnCombatDamageToPlayer();
			break;
		case ON_CREATURE_DEATH:
			abilityTrigger = new OnCreatureDeath();
			break;
		case ON_ENTER_BATTLEFIELD:
			abilityTrigger = new OnEnterBattlefield();
			break;
		case ON_PLAYER_CAST:
			abilityTrigger = new OnPlayerCast();
			break;
		case ON_SPELL_TARGET:
			abilityTrigger = new OnSpellTarget();
			break;
		case ON_SUSPEND_TIME_COUNTER_REDUCED:
			abilityTrigger = new OnSuspendTimeCounterReduced();
			break;
		case ON_UN_SUSPEND:
			abilityTrigger = new OnUnSuspend();
			break;
		case ON_UPKEEP_PLAYER:
			abilityTrigger = new OnUpkeepPlayer();
			break;
		case ON_BLOCKED:
			abilityTrigger = new OnBlocked();
			break;
		case PASSIVE:
			abilityTrigger = new Passive();
			break;
		}

		return abilityTrigger;
	}

	public String toString(Object value) {
		if (value instanceof JSONArray) {
			return ((JSONArray) value).get(0).toString().replace("\"", "");
		} else {
			return value.toString().replace("\"", "");
		}
	}

	private Enchantment loadEnchantement(CardData card) throws SystemException {

		Set<Color> cardColors = calculateCardColors(card);

		ManaCost manaCost = calculateManaCost(card.getManacost());

		Set<Behaviour> behaviours = new HashSet<Behaviour>();
		Set<AbilityData> abilities = card.getAbilities();
		Set<Ability> creatureAbilities = new HashSet<Ability>();
		for (AbilityData a : abilities) {

			Ability creatAbility = createAbility(a);
			if (creatAbility != null) {
				creatureAbilities.add(creatAbility);
			} else {
				System.out.println("ability not found " + a.getId() + " " + a.getAbility());
			}
		}
		for (Ability ability : creatureAbilities) {
			if (ability.getTrigger() instanceof OnUnSuspend
					|| ability.getTrigger() instanceof OnSuspendTimeCounterReduced) {
				Suspend suspenAbility = null;
				for (Ability ability2 : creatureAbilities) {
					if (ability2 instanceof Suspend) {
						suspenAbility = (Suspend) ability2;
					}
				}
				if (ability.getTrigger() instanceof OnUnSuspend) {
					((OnUnSuspend) ability.getTrigger()).setSuspendAbility(suspenAbility);
				} else if (ability.getTrigger() instanceof OnSuspendTimeCounterReduced) {
					((OnSuspendTimeCounterReduced) ability.getTrigger()).setSuspendAbility(suspenAbility);
				}
			}
		}
		return new Enchantment(card.getMultiverseid(), card.getName(), card.getText(), cardColors, behaviours,
				creatureAbilities, manaCost);

	}

	private BasicLand loadLand(CardData card) throws SystemException {

		Set<Color> cardColors = calculateCardColors(card);

		ManaCost manaCost = calculateManaCost(card.getManacost());

		Set<Behaviour> behaviours = new HashSet<Behaviour>();
		Set<AbilityData> abilities = card.getAbilities();
		Set<Ability> creatureAbilities = new HashSet<Ability>();
		for (AbilityData a : abilities) {

			Ability creatAbility = createAbility(a);
			if (creatAbility != null) {
				creatureAbilities.add(creatAbility);
			} else {
				System.out.println("ability not found " + a.getId() + " " + a.getAbility());
			}
		}
		for (Ability ability : creatureAbilities) {
			if (ability.getTrigger() instanceof OnUnSuspend
					|| ability.getTrigger() instanceof OnSuspendTimeCounterReduced) {
				Suspend suspenAbility = null;
				for (Ability ability2 : creatureAbilities) {
					if (ability2 instanceof Suspend) {
						suspenAbility = (Suspend) ability2;
					}
				}
				if (ability.getTrigger() instanceof OnUnSuspend) {
					((OnUnSuspend) ability.getTrigger()).setSuspendAbility(suspenAbility);
				} else if (ability.getTrigger() instanceof OnSuspendTimeCounterReduced) {
					((OnSuspendTimeCounterReduced) ability.getTrigger()).setSuspendAbility(suspenAbility);
				}
			}
		}
		return new BasicLand(card.getMultiverseid(), card.getName(), cardColors, behaviours, creatureAbilities,
				manaCost);

	}

	private Instant loadInstant(CardData card) throws SystemException {

		Set<Color> cardColors = calculateCardColors(card);

		ManaCost manaCost = calculateManaCost(card.getManacost());

		Set<Behaviour> behaviours = new HashSet<Behaviour>();
		Set<AbilityData> abilities = card.getAbilities();
		Set<Ability> creatureAbilities = new HashSet<Ability>();
		for (AbilityData a : abilities) {

			Ability creatAbility = createAbility(a);
			if (creatAbility != null) {
				creatureAbilities.add(creatAbility);
			} else {
				System.out.println("ability not found " + a.getId() + " " + a.getAbility());
			}
		}
		for (Ability ability : creatureAbilities) {
			if (ability.getTrigger() instanceof OnUnSuspend
					|| ability.getTrigger() instanceof OnSuspendTimeCounterReduced) {
				Suspend suspenAbility = null;
				for (Ability ability2 : creatureAbilities) {
					if (ability2 instanceof Suspend) {
						suspenAbility = (Suspend) ability2;
					}
				}
				if (ability.getTrigger() instanceof OnUnSuspend) {
					((OnUnSuspend) ability.getTrigger()).setSuspendAbility(suspenAbility);
				} else if (ability.getTrigger() instanceof OnSuspendTimeCounterReduced) {
					((OnSuspendTimeCounterReduced) ability.getTrigger()).setSuspendAbility(suspenAbility);
				}
			}
		}
		return new Instant(card.getMultiverseid(), card.getName(), card.getText(), cardColors, behaviours,
				creatureAbilities, manaCost);

	}

	private Sorcery loadSorcery(CardData card) throws SystemException {

		Set<Color> cardColors = calculateCardColors(card);

		ManaCost manaCost = calculateManaCost(card.getManacost());

		Set<Behaviour> behaviours = new HashSet<Behaviour>();
		Set<AbilityData> abilities = card.getAbilities();
		Set<Ability> creatureAbilities = new HashSet<Ability>();
		for (AbilityData a : abilities) {

			Ability creatAbility = createAbility(a);
			if (creatAbility != null) {
				creatureAbilities.add(creatAbility);
			} else {
				System.out.println("ability not found " + a.getId() + " " + a.getAbility());
			}
		}
		for (Ability ability : creatureAbilities) {
			if (ability.getTrigger() instanceof OnUnSuspend
					|| ability.getTrigger() instanceof OnSuspendTimeCounterReduced) {
				Suspend suspenAbility = null;
				for (Ability ability2 : creatureAbilities) {
					if (ability2 instanceof Suspend) {
						suspenAbility = (Suspend) ability2;
					}
				}
				if (ability.getTrigger() instanceof OnUnSuspend) {
					((OnUnSuspend) ability.getTrigger()).setSuspendAbility(suspenAbility);
				} else if (ability.getTrigger() instanceof OnSuspendTimeCounterReduced) {
					((OnSuspendTimeCounterReduced) ability.getTrigger()).setSuspendAbility(suspenAbility);
				}
			}
		}
		return new Sorcery(card.getMultiverseid(), card.getName(), card.getText(), cardColors, behaviours,
				creatureAbilities, manaCost);

	}

	private Artifact loadArtifact(CardData card) throws SystemException {

		Set<Color> cardColors = calculateCardColors(card);

		ManaCost manaCost = calculateManaCost(card.getManacost());

		Set<Behaviour> behaviours = new HashSet<Behaviour>();
		Set<AbilityData> abilities = card.getAbilities();
		Set<Ability> creatureAbilities = new HashSet<Ability>();
		for (AbilityData a : abilities) {

			Ability creatAbility = createAbility(a);
			if (creatAbility != null) {
				creatureAbilities.add(creatAbility);
			} else {
				System.out.println("ability not found " + a.getId() + " " + a.getAbility());
			}
		}
		for (Ability ability : creatureAbilities) {
			if (ability.getTrigger() instanceof OnUnSuspend
					|| ability.getTrigger() instanceof OnSuspendTimeCounterReduced) {
				Suspend suspenAbility = null;
				for (Ability ability2 : creatureAbilities) {
					if (ability2 instanceof Suspend) {
						suspenAbility = (Suspend) ability2;
					}
				}
				if (ability.getTrigger() instanceof OnUnSuspend) {
					((OnUnSuspend) ability.getTrigger()).setSuspendAbility(suspenAbility);
				} else if (ability.getTrigger() instanceof OnSuspendTimeCounterReduced) {
					((OnSuspendTimeCounterReduced) ability.getTrigger()).setSuspendAbility(suspenAbility);
				}
			}
		}
		return new Artifact(card.getMultiverseid(), card.getName(), card.getText(), cardColors, behaviours,
				creatureAbilities, manaCost);

	}

	private Planeswalker loadPlaneswalker(CardData card) throws SystemException {

		Set<Color> cardColors = calculateCardColors(card);

		ManaCost manaCost = calculateManaCost(card.getManacost());

		Set<Behaviour> behaviours = new HashSet<Behaviour>();
		int loyalty = card.getLoyalty();
		Set<AbilityData> abilities = card.getAbilities();
		Set<Ability> creatureAbilities = new HashSet<Ability>();
		for (AbilityData a : abilities) {

			Ability creatAbility = createAbility(a);
			if (creatAbility != null) {
				creatureAbilities.add(creatAbility);
			} else {
				System.out.println("ability not found " + a.getId() + " " + a.getAbility());
			}
		}
		for (Ability ability : creatureAbilities) {
			if (ability.getTrigger() instanceof OnUnSuspend
					|| ability.getTrigger() instanceof OnSuspendTimeCounterReduced) {
				Suspend suspenAbility = null;
				for (Ability ability2 : creatureAbilities) {
					if (ability2 instanceof Suspend) {
						suspenAbility = (Suspend) ability2;
					}
				}
				if (ability.getTrigger() instanceof OnUnSuspend) {
					((OnUnSuspend) ability.getTrigger()).setSuspendAbility(suspenAbility);
				} else if (ability.getTrigger() instanceof OnSuspendTimeCounterReduced) {
					((OnSuspendTimeCounterReduced) ability.getTrigger()).setSuspendAbility(suspenAbility);
				}
			}
		}
		return new Planeswalker(card.getMultiverseid(), card.getName(), card.getText(), cardColors, behaviours,
				creatureAbilities, loyalty, manaCost);

	}

	private Set<Color> calculateCardColors(CardData card) {
		Set<Color> cardColors = new HashSet<Color>();
		Set<ColorData> colors = card.getColors();
		for (ColorData color : colors) {
			String cardColor = color.getColor().toUpperCase();
			if (cardColor != null) {
				cardColors.add(com.wimcorp.magic.color.Color.valueOf(cardColor));
			} else {
				cardColors.add(com.wimcorp.magic.color.Color.NEUTRAL);
			}
		}
		return cardColors;
	}

	private ManaCost calculateManaCost(String mc) {
		ManaCost manaCost = new ManaCost();
		if (mc != null) {
			Pattern p = Pattern.compile("\\{(.*?)\\}");
			Matcher m = p.matcher(mc);

			while (m.find()) {
				if ("U".equals(m.group(1))) {
					manaCost.addMana(Color.BLUE);
				} else if ("G".equals(m.group(1))) {
					manaCost.addMana(Color.GREEN);
				} else if ("B".equals(m.group(1))) {
					manaCost.addMana(Color.BLACK);
				} else if ("R".equals(m.group(1))) {
					manaCost.addMana(Color.RED);
				} else if ("W".equals(m.group(1))) {
					manaCost.addMana(Color.WHITE);
				} else {
					for (int i = 0; i < new Integer(m.group(1)); i++) {
						manaCost.addMana(Color.NEUTRAL);
					}
				}

			}
		}
		return manaCost;
	}

}
